package com.springmvc.context;

import com.springmvc.annotation.AutoWired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.Service;
import com.springmvc.exception.ContextException;
import com.springmvc.xml.XmlParser;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @Author Lao Wang
 * @Date 2023/11/27
 * @Description spring容器
 **/
public class WebApplicationContext {
    //根据web.xml的配置拿到springmvc.xml相关配置
    String contextConfigLocation;
    List<String> classNameList =  new ArrayList<String>();
    //spring ioc容器
    public Map<String,Object> iocMap = new ConcurrentHashMap<String,Object>();

    public WebApplicationContext(String contextConfigLocation) {
        this.contextConfigLocation = contextConfigLocation;
    }

    //初始化spring容器方法
    public void refresh(){
        //1.DOM4J解析springmvc.xml文件
        String basePackage = XmlParser.getBasePackage(contextConfigLocation.split(":")[1]);
        String[] basePackages = basePackage.split(",");
        if (basePackages.length>0) {
            //com.wsir.service、com.wsir.controller
            for (String pack :basePackages) {
                excuteScanPackage(pack);
            }
        }
        System.out.println("扫描结果："+classNameList);
        //扫描到之后实例化spring容器中的bean
        excuteInstance();
        //输出一下ioc容器中的对象
        System.out.println("spring容器中的对象有："+iocMap);
        //实现容器中对象的注入 @Autowired UserService userService
        excuteAutoWired();

    }
    //实例化spring容器中的bean对象
    public void excuteInstance(){
        if (classNameList.size() == 0) {
            //如果没有扫描到需要实例化的类
            throw new ContextException("没有要实例化的类");
        }
        try {
            for (String className :classNameList) {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(Controller.class)) {
                    //控制层的类com.wsir.controller.UserController->userController 控制层对象的名称
                    String beanName =
                            clazz.getSimpleName().substring(0,1).toLowerCase()+clazz.getSimpleName().substring(1);
                    try {
                        iocMap.put(beanName,clazz.getDeclaredConstructor().newInstance());
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                } else if (clazz.isAnnotationPresent(Service.class)) {
                    try {
                        //com.wsir.service.impl.UserServiceIml
                        Service serviceAnnotation = clazz.getAnnotation(Service.class);
                        String beanName = serviceAnnotation.value();
                        if ("".equals(beanName)) {
                            //Service注解如果没配置name属性，则直接用类名
                            Class<?>[] interfaces = clazz.getInterfaces();
                            for (Class<?> c1 : interfaces){
                                String beanName1 = c1.getSimpleName().substring(0,1).toLowerCase()+c1.getSimpleName().substring(1);
                                iocMap.put(beanName1,clazz.getDeclaredConstructor().newInstance());
                            }
                        }else {
                            //如果Service注解有配置name属性，则直接用name名实例化
                            iocMap.put(beanName,clazz.getDeclaredConstructor().newInstance());
                        }
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    public void excuteScanPackage(String pack){
        //扫描包，根据springmvc.xml中的包名生成url,/com/wsir/service
        URL url = this.getClass().getClassLoader().getResource("/"+pack.replaceAll("\\.","/"));
        //把url转成文件访问路径，拿到文件
        String path = url.getFile();
        //转成文件路径
        File dir = new File(path);
        for (File f :dir.listFiles()) {
            if (f.isDirectory()) {
                //如果是一个文件目录，递归继续，遍历到具体文件为止。如：com.wsir.service.impl
                excuteScanPackage(pack+"."+f.getName());
            }else {
                //如果是文件，获取全路径，去掉文件.class后缀，只取类名，以后实例化用。如：com.wsir.service.impl.UserService
                String className = pack+"."+f.getName().replaceAll(".class","");
                classNameList.add(className);
            }
        }
    }
    //实现spring容器中对象的依赖注入
    public void excuteAutoWired(){
        try {
            if (iocMap.isEmpty()) {
                throw new ContextException("没有找到对应的初始化bean对象");
            }
            //迭代ioc容器
            for (Map.Entry<String,Object> entry:iocMap.entrySet()) {
                String key = entry.getKey();
                Object bean = entry.getValue();
                Field[] declaredFields = bean.getClass().getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    if (declaredField.isAnnotationPresent(AutoWired.class)){
                        AutoWired autowiredAnnotation = declaredField.getAnnotation(AutoWired.class);
                        String beanName = autowiredAnnotation.value();
                        if ("".equals(beanName)){
                            Class<?> type = declaredField.getType();
                            beanName = type.getSimpleName().substring(0,1).toLowerCase()+type.getSimpleName().substring(1);
                        }
                        //有可能是私有字段，暴力反射一下
                        declaredField.setAccessible(true);
                        //调用反射将容器中实例化好的对象注入到对应层中的属性(赋值)
                        declaredField.set(bean,iocMap.get(beanName));
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
